/*
 * Copyright 2009-2010 the original author or authors.
 *
 * Licensed under the Apache license, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.internna.iwebjtracker.model;

import java.util.Date;
import java.util.List;
import java.util.Locale;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import org.directwebremoting.annotations.DataTransferObject;
import org.directwebremoting.annotations.RemoteProperty;
import org.hibernate.validator.NotNull;
import org.internna.iwebmvc.crypto.Cipherer;
import org.internna.iwebmvc.model.rss.RSSItem;
import org.internna.iwebjtracker.activity.model.RecordedActivity;
import org.internna.iwebmvc.metadata.ui.EntityFilter;
import org.internna.iwebmvc.metadata.ui.GridColumn;
import org.internna.iwebmvc.model.AbstractOwnedDomainEntity;
import org.internna.iwebmvc.model.I18nText;
import org.internna.iwebmvc.model.Rating;
import org.internna.iwebmvc.model.rss.Feedable;
import org.springframework.context.MessageSource;
import org.springframework.core.style.ToStringCreator;

import static org.internna.iwebmvc.utils.StringUtils.removeISO88591Entities;

/**
 * Records important operations on other entities.
 *
 * @author Jose Noheda
 * @since 1.0
 */
@Entity
@Table(name = "ACTIVITY")
@NamedQueries({
    @NamedQuery(name = "Activity.findAll", query = "SELECT a FROM Activity a"),
    @NamedQuery(name = "Activity.findLast", query = "SELECT a FROM Activity a ORDER by eventDate DESC"),
    @NamedQuery(name = "Activity.findLastForIssue", query = "SELECT a FROM Activity a WHERE issue.id = :issue ORDER by eventDate DESC"),
    @NamedQuery(name = "Activity.findLastForProject", query = "SELECT a FROM Activity a WHERE project.id = :project ORDER by eventDate DESC"),
    @NamedQuery(name = "Activity.findLastForUser", query = "SELECT a FROM Activity a WHERE :user IN ELEMENTS(project.observers) ORDER by eventDate DESC")
})
@DataTransferObject(javascript = "Activity")
public class Activity extends AbstractOwnedDomainEntity implements RecordedActivity, Feedable {

    private static final long serialVersionUID = -1573614325691841307L;

    /**
     * Named query to retrieved all the activity in this IWebJTracker instance ordered by date.
     */
    public static final String FIND_LAST_ACTIVITY = "Activity.findLast";

    /**
     * Named query to retrieved all the activity for a user ordered by date.
     */
    public static final String FIND_LAST_ACTIVITY_FOR_USER = "Activity.findLastForUser";

    /**
     * Named query to retrieved all the activity for a specific issue ordered by date.
     */
    public static final String FIND_LAST_ACTIVITY_FOR_ISSUE = "Activity.findLastForIssue";

    /**
     * Named query to retrieved all the activity for a project ordered by date.
     */
    public static final String FIND_LAST_ACTIVITY_FOR_PROJECT = "Activity.findLastForProject";

    /**
     * Fills default values for event date and priority.
     */
    public Activity() {
        super();
        eventDate = new Date();
        activityPriority = new Rating();
    }

    @NotNull
    @RemoteProperty
    @GridColumn(order = 2)
    @EntityFilter(type = EntityFilter.WIDGET_TYPE.ENUM)
    @Column(name = "ACTIVITY_TYPE", nullable = false, updatable = false)
    private ActivityType type;

    @NotNull
    @RemoteProperty
    @Temporal(TemporalType.TIMESTAMP)
    @GridColumn(order = 3, relativeWidth = 1D)
    @Column(name = "EVENT_DATE", nullable = false, updatable = false)
    @EntityFilter(order = 1, type = EntityFilter.WIDGET_TYPE.DATE, range = true)
    private Date eventDate;

    @NotNull
    @RemoteProperty
    @GridColumn(order = 5, relativeWidth = 2D)
    @EntityFilter(order = 3, type = EntityFilter.WIDGET_TYPE.ENTITY)
    @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
    @JoinColumn(name = "PROJECT", nullable = false, updatable = false)
    private DefaultProject project;

    @RemoteProperty
    @GridColumn (order = 6)
    @JoinColumn(name = "ISSUE", updatable = false)
    @EntityFilter(order = 4, type = EntityFilter.WIDGET_TYPE.ENTITY)
    @ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
    private DefaultIssue issue;

    @Embedded
    @RemoteProperty
    @GridColumn(relativeWidth = 0.6)
    @EntityFilter(order = 5, type = EntityFilter.WIDGET_TYPE.RATING, range = true)
    private Rating activityPriority;

    @RemoteProperty
    @ManyToOne(cascade = {CascadeType.ALL})
    @GridColumn (order = 4, relativeWidth = 0.9)
    @JoinColumn(name = "ORIGINATOR", updatable = false)
    @EntityFilter(order = 2, type = EntityFilter.WIDGET_TYPE.ENTITY)
    private TrackerUser originator;

    /**
     * Accessor method
     */
    public Date getEventDate() {
        return eventDate == null ? null : new Date(eventDate.getTime());
    }

    /**
     * Accessor method
     */
    protected void setEventDate(Date eventDate) {
        this.eventDate = eventDate;
    }

    /**
     * Accessor method
     */
    public ActivityType getType() {
        return type;
    }

    /**
     * Accessor method
     */
    public void setType(ActivityType type) {
        this.type = type;
        if (type != null) {
            getActivityPriority().setRating(getType().getPriority());
        }
    }

    /**
     * Accessor method
     */
    public DefaultProject getProject() {
        return project;
    }

    /**
     * Accessor method
     */
    public void setProject(DefaultProject project) {
        this.project = project;
    }

    /**
     * Accessor method
     */
    public Rating getActivityPriority() {
        return activityPriority;
    }

    /**
     * Accessor method
     */
    protected void setActivityPriority(Rating activityPriority) {
        this.activityPriority = activityPriority;
    }

    /**
     * Accessor method
     */
    public DefaultIssue getIssue() {
        return issue;
    }

    /**
     * Adds an issue as long as it's null or has a project that matches the one already configured.
     *
     * @param issue any
     */
    public void setIssue(DefaultIssue issue) {
        if ((issue == null) || ((issue.getParentProject() != null) && (issue.getParentProject().equals(this.project)))) {
            this.issue = issue;
        } else if ((issue.getParentProject() != null) && (this.project == null)) {
            this.issue = issue;
            this.project = issue.getParentProject();
        }
    }

    /**
     * Accessor method
     */
    public TrackerUser getOriginator() {
        return originator;
    }

    /**
     * Accessor method
     */
    public void setOriginator(TrackerUser originator) {
        this.originator = originator;
    }

    /**
     * Show activity type in widgets.
     *
     * @param locale any
     * @return an empty string unless type has value
     */
    @Override public final String getCaption(Locale locale) {
        return type == null ? "" : type.toString();
    }

    /**
     * For logging purposes mainly.
     *
     * @return strign built using Spring styling
     */
    @Override public final String toString() {
        ToStringCreator creator = new ToStringCreator(this);
        creator.append("type", type);
        return creator.toString();
    }

    /**
     * Generates a valid i18n RSS feed item from this activity.
     *
     * @param cipherer a non null instance
     * @param locales a non null non empty list
     * @param messageResolver a non null instance
     * @return a non null object
     */
    @Override public final RSSItem toRSS(Cipherer cipherer, List<Locale> locales, MessageSource messageResolver) {
        RSSItem rss = new RSSItem();
        rss.setDate(getEventDate());
        rss.setAuthor(getOriginator());
        rss.setGuid(getId().toString());
        rss.setTitle(new I18nText());
        rss.setDescription(new I18nText());
        for (Locale locale : locales) {
            rss.getTitle().add(locale, removeISO88591Entities(messageResolver.getMessage("RSS." + getType(), null, locale)));
            if (getIssue() != null) {
                rss.getDescription().add(locale, issue.getAlias() + ": " + removeISO88591Entities(issue.getSummary()));
            } else {
                rss.getDescription().add(locale, project.getProjectName() + "[" + project.getHomePage() + "]");
            }
        }
        return rss;
    }

}
